#!/usr/bin/perl
our $Version;
use warnings;
use strict;

#    Pete's Automated VGAP Hosting Thingy
#    Copyright (C) 1997-2003 Peter Harris
$    Version = '0.2.2';
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License version 2 as
#    published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    This program is currently hosted at http://www.agamemnon.net/vgap/

use Net::IMAP::Simple;
use MIME::Parser;
use MIME::Base64;
use Net::SMTP;
use IniConf;
$main::TZ='EST5EDT';
use Date::Manip;
use Digest::SHA1 'sha1_base64';
use Cwd;

config MIME::ToolUtils DEBUGGING => 0;

# needed
our $ininame;
our $mainini;
our ($NumberOfGames,@ini);
our %UnShortName;
our ($maininitime, @initime);
our $running;

# wanted for optimization reasons: These are used every $MainDelay seconds.
our ($DebugLog,$DebugCon,$LogFileName);
our ($POPhost, $POPuser, $POPpw);
our $MainDelay;

sub GetSalt {
   my $game = shift;
   my $salt = $ini[$game]->val('General','Salt');
   if (!defined($salt)) {
      $salt = '';
      $salt .= ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64] foreach(1..8);
      $ini[$game]->newval('General','Salt',$salt);
      UpdateConfig($game);
   }
   return $salt;
}

sub GenerateCookie {
   my $game = shift;
   my $type = shift;
   my $email = shift;
   my $salt = GetSalt($game);

   return sha1_base64($salt, $type, $email);

   # A crypt based implementation would probably want to
   # use <the first letter of $type>.$email , $salt
   # And then trim the first two letters (the salt) from
   # the front of the digest before returning it.
}

sub EnvelopeTo {
   my $to = shift;
   my $subject = shift;

   logit(6,"Generating a message to $to");
   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   my $from = $mainini->val('General','VGAPEmail');
   $smtp->datasend("From: Pete\'s Automated VGAP Thingy <$from>\n");
   $smtp->datasend("To: $to\n");
   $smtp->datasend("Subject: [VGA Planets] $subject\n");
   $smtp->datasend("\n");

   return $smtp;
}

sub RaceNames {
   my @races = undef;
   my $game = shift;
   if (open FILE, $ini[$game]->val('General','Homedir').'race.nm' or
       open FILE, $ini[$game]->val('General','VGAPdir').'race.nm') {
      my $glob;
      $glob = <FILE>;
      close FILE;
      @races=unpack 'A30'x 11, $glob;
   } else {
      logit(1,"race.nm not found, assuming defaults.");
      @races = ('The Solar Federation',
                'The Lizards',
                'The Bird Men',
                'The Fascists',
                'The Privateers',
                'The Cyborg',
                'The Crystal People',
                'The Evil Empire',
                'The Robots',
                'The Rebels',
                'The Lost Colonies of Man');
   }
   return @races;
}

sub invite {
   my $to = shift;
   my $game = shift;

   logit(4,"$to is being invited to $game.");

   my $shortName = $ini[$game]->val('General','ShortName');
   my $longName = $ini[$game]->val('General','LongName');
   my $smtp = EnvelopeTo($to, 'Invitation Message');
   $smtp->datasend("Congratulations! You have been invited to join a VGA Planets game!\n");
   $smtp->datasend("The game is called $longName.\n");
   $smtp->datasend("Use the short name '$shortName' in commands.\n\n");
   $smtp->datasend("This message is being sent to you by an automated VGA Planets system\nIf you have never used the system before, plese send a message with\nthe subject 'VGAP' and the word 'help' on a line by itself in the body\nto the sender of this message. (Just hit 'reply')\n\n");

   if (uc($ini[$game]->val('General','Status')) eq 'INVITATION ONLY') {
      $smtp->datasend("This is an invitation-only game. You must include the following\nline in your message before your 'join' command:\n\n");
      $smtp->datasend('cookie '.GenerateCookie($game,'invite',$to)."\n");
   }

   $smtp->dataend();
   $smtp->quit;   
}

sub resign {
   my $player = shift;
   my $game = shift;
   my $resigncookie = GenerateCookie($game, 'resign', $player);
   my $to = $ini[$game]->val("Player$player", 'Email');
   my $smtp;

   if (!defined($main::cookie) || ($main::cookie ne $resigncookie) ) {
      logit (3,"$to is trying to resign from player $player of game $game.");
      $smtp = EnvelopeTo($to, 'So you want to quit, eh?');

      $smtp->datasend("Resign command detected.\n\n");
      $smtp->datasend("So you want to quit, eh? Well, just to be sure, you have\n");
      $smtp->datasend("to send the resign command agin. This time, however, you\n");
      $smtp->datasend("must include the following line in your message before 'resign',\n");
      $smtp->datasend("in addition to your usual 'auth' command:\n\n");
      $smtp->datasend("cookie $resigncookie\n");
      $smtp->datasend("\n\nBe very, very certain you want to quit before doing this.\n");
      $smtp->datasend("Resigning is permanent and irreversable. There is no 'unresign' command.\n");
      $smtp->datasend("\n\nIf you sent the resign command by accident, simply\n");
      $smtp->datasend("do nothing. Your friendly neighborhood saftey net has caught\n");
      $smtp->datasend("the mistake for you. You're welcome.\n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   logit (3,"$to (player $player) has confirmed their resignation from game $game.");

   $smtp = EnvelopeTo($to, "Goodbye, player $player of ".$ini[$game]->val('General','ShortName'));
   $smtp->datasend("Resign command sucessful.\n\n");
   $smtp->datasend("You have been removed from this game.\n");
   $smtp->dataend();
   $smtp->quit;

   $ini[$game]->setval("Player$player",'Active','NO');
   UpdateConfig($game);
}

sub forcerun {
   my $smtp;
   my $game = shift;
   my $runcookie = GenerateCookie($game, 'run', TurnNumber($game));
   my $to = $ini[$game]->val('Admin', 'Email');
   my $shortname = $ini[$game]->val('General','ShortName');

   if ( (!defined $main::cookie) || ($main::cookie ne $runcookie) ) {
      logit (3,"An Admin of game $game is requesting a ForceRun, sending cookie.");
      $smtp = EnvelopeTo($to, 'So you want to run now, eh?');

      $smtp->datasend("ForceRun command detected.\n\n");
      $smtp->datasend("So you want to run $shortname now, eh?\n");
      $smtp->datasend("Well, just to be sure, you have\n");
      $smtp->datasend("to send the forcerun command agin. This time, however, you\n");
      $smtp->datasend("must include the following line in your message before 'forcerun':\n\n");
      $smtp->datasend("cookie $runcookie\n");
      $smtp->datasend("\n\nBe very, very certain you want to run this turn before doing this.\n");
      $smtp->datasend("Running is permanent and irreversable. There is no 'unrun' command.\n");
      $smtp->datasend("\n\nIf you sent the forcerun command by accident, simply\n");
      $smtp->datasend("do nothing. Your friendly neighborhood saftey net has caught\n");
      $smtp->datasend("the mistake for you. You're welcome.\n");

      $smtp->datasend("\n\nThis cookie is only valid for this turn. If you try to forcerun\n");
      $smtp->datasend("again in the future, I will send you a new cookie.\n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   logit (3,"An Admin of game $game has verified ForceRun with valid cookie.");

   $smtp = EnvelopeTo($to, "Running $shortname now.");
   $smtp->datasend("ForceRun command sucessful.\n\n");
   $smtp->dataend();
   $smtp->quit;

   rungame($game,1);
}

sub killrace {
   my $player = shift;
   my $game = shift;
   my $killcookie = GenerateCookie($game, 'kill', $player);
   my $to = $ini[$game]->val('Admin', 'Email');
   my @races = RaceNames($game);
   my $smtp;

   if ( (!defined $main::cookie) || ($main::cookie ne $killcookie) ) {
      logit (3,"Admin of game $game desires to kill player $player, sending verification cookie.");
      $smtp = EnvelopeTo($to, 'So you want to kill, eh?');

      $smtp->datasend("Kill command detected.\n\n");
      $smtp->datasend("So you want to kill $races[$player-1], eh?\n");
      $smtp->datasend("Well, just to be sure, you have\n");
      $smtp->datasend("to send the kill command agin. This time, however, you\n");
      $smtp->datasend("must include the following line in your message before 'kill foo':\n\n");
      $smtp->datasend("cookie $killcookie\n");
      $smtp->datasend("\n\nBe very, very certain you want to kill before doing this.\n");
      $smtp->datasend("Killing is permanent and irreversable. There is no 'unkill' command.\n");
      $smtp->datasend("\n\nIf you sent the kill command by accident, simply\n");
      $smtp->datasend("do nothing. Your friendly neighborhood saftey net has caught\n");
      $smtp->datasend("the mistake for you. You're welcome.\n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   logit (3,"Admin of game $game has verified the death of $player.");

   $smtp = EnvelopeTo($to, "Player $player of ".$ini[$game]->val('General','ShortName')." is dead, Jim.");
   $smtp->datasend("Kill command sucessful.\n\n");
   $smtp->datasend("$races[$player-1] has been removed from this game.\n");
   $smtp->dataend();
   $smtp->quit;

   $to = $ini[$game]->val("Player$player", 'Email');
   $smtp = EnvelopeTo($to, "Goodbye, player $player of ".$ini[$game]->val('General','ShortName'));
   $smtp->datasend("The administrator of this game has seen fit to remove you from\n");
   $smtp->datasend("the game. You are no longer playing in ".$ini[$game]->val('General','ShortName')."\n");
   $smtp->datasend("\nGoodbye. So long. Farewell. This is your last and final message.\n");
   $smtp->dataend();
   $smtp->quit;

   $ini[$game]->setval("Player$player",'Active','NO');
   UpdateConfig($game);
}

sub helpto { # add warnings et al here.
   my $to = shift;

   logit(4,"$to is requesting some help.");

   my $smtp = EnvelopeTo($to, 'Help Message');

   $smtp->datasend("No valid commands found in your message.\n\n") if (shift);
   $smtp->datasend("This is Pete's Automated VGAP Thingy, Version $Version\n\n");
   $smtp->datasend("The thingy will only process email if one of 'VGAP' or 'VGA Planets'\n is in the subject line somewhere.\n\n");
   $smtp->datasend("Commands currently supported by the VGAP thingy:\n");
   $smtp->datasend("   admin    - mails your message to the VGAP thingy's administrator.\n");
   $smtp->datasend("              implies end. See end.\n");
   $smtp->datasend("            - If your message has anything to do with a game in progress,\n");
   $smtp->datasend("              please issue a valid auth command first.\n");
   $smtp->datasend("   auth     - authorization request. Required before any commands\n");
   $smtp->datasend("              such as comp, email, pass, submit, resend, or quit.\n");
   $smtp->datasend("            - uses the form GAME:RACE#:PASSWORD\n");
   $smtp->datasend("              note that (unlike most other things) passwords are\n");
   $smtp->datasend("              case-sensitive.\n");
   $smtp->datasend("   check    - like desc, only it sends you a list of who has/has not\n");
   $smtp->datasend("              submitted their turn file.\n");
   $smtp->datasend("   comp     - turns on or off the computer player for your race.\n");
   $smtp->datasend("              Use this if you are going to be away for a short while and\n");
   $smtp->datasend("              you want the computer to play your turns for you.\n");
   $smtp->datasend("            - The computer will only play your turn if you haven't\n");
   $smtp->datasend("              submitted a valid .TRN file already.\n");
   $smtp->datasend("            - Setting 'comp on' will disable warnings, but will not\n");
   $smtp->datasend("              disable .RST mailings.\n");
   $smtp->datasend("            - e.g.:   comp on\n");
   $smtp->datasend("                      comp off\n");
   $smtp->datasend("   desc     - mails you a description of a game. eg: desc game1\n");
   $smtp->datasend("   email    - changes the e-mail address that the VGAP thingy sends your\n");
   $smtp->datasend("              .RST files to. eg. 'email foobar\@hotmail.com'.\n");
   $smtp->datasend("   end      - end of commands. Useful if you have a signature that\n");
   $smtp->datasend("              contains words that might be mistaken for commands.\n");
   $smtp->datasend("   help     - mails this list to you.\n");
   $smtp->datasend("   join     - request to join a game. Include a password, followed by,\n");
   $smtp->datasend("              in order of preference, a list of races you might\n");
   $smtp->datasend("              want to play.\n");
   $smtp->datasend("              eg.  join GAME1:password:2:7:1  means you want to join GAME1,\n");
   $smtp->datasend("              as the Lizards. Or the Crystals. Or the Federation. In that order.\n");
   $smtp->datasend("   list     - mails you a list of all currently active games.\n");
   $smtp->datasend("   pass     - changes the password that you use for the auth command.\n");
   $smtp->datasend("              Requires a successful auth first.\n");
   $smtp->datasend("   resend   - mails you a copy of your .RST file. Mostly only useful\n");
   $smtp->datasend("              if you manage to lose your original copy.\n");
   $smtp->datasend("   resign   - Quit the auth'd game. Permanent and irreversable.\n");
   $smtp->datasend("   submit   - indicates to the VGAP thingy that your .TRN file is attached to\n");
   $smtp->datasend("              this e-mail. If you submit more than once in a given turn, only\n");
   $smtp->datasend("              most recently submitted turn file will be used.\n");
   $smtp->datasend("              NOTE: You MUST send a vaild auth command BEFORE you send a submit.\n");
   $smtp->datasend("   unsubmit - You made a mistake. You're fixing it. You don't want the\n");
   $smtp->datasend("              turn to run just yet. This command undoes your submit.\n");
   $smtp->datasend("\n Note that each command must appear on a line by itself.\n");

   $smtp->dataend();
   $smtp->quit;
}

sub logit {
   my $level=shift;
   my $now = ParseDate('today');
   my @logentry = @_;

   chomp(@logentry);
   if ($level <= $DebugLog) {
      open FILE,">>$LogFileName" or print "Can't open LogFile $LogFileName because $!\n";
      print FILE "[$now] ",@logentry,"\n";
      close FILE;
   }

   if ($level <= $DebugCon) {
      print "[$now] ",@logentry,"\n";
   }
}

sub listto {
   my $out;
   my $to = shift;

   logit(4,"$to is asking for a list");

   my $smtp = EnvelopeTo($to, 'List of Active Games');

   $out = sprintf("%-12s%-40s%-16s\n",'Name','Long Name','Status');
   $smtp->datasend($out);
   $out = sprintf("%-12s%-40s%-16s\n",'----------','--------------------------------------','---------------');
   $smtp->datasend($out); 

   if ($NumberOfGames < 1) {
      $smtp->datasend("No active games\n");
   } else {
      foreach my $game (1..$NumberOfGames) {
         $out = sprintf("%-12s%-40s%-16s\n",
            $ini[$game]->val('General','ShortName'),
            $ini[$game]->val('General','LongName'),
            $ini[$game]->val('General','Status') );
         $smtp->datasend($out);
      }
   }

   $smtp->dataend();
   $smtp->quit;
}

sub descto {
   my $to = shift;
   my $game = shift;
   my $auth = shift;
   my $authgame = shift;

   my $shortname =$ini[$game]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Description of Game $shortname");

   logit(4,"$to is asking for a desc of $shortname");

   my @description=$ini[$game]->val('General','Description');
   my $temp;
   $smtp->datasend("You have requested a description for VGAP game: $shortname\n\n");
   $temp = $ini[$game]->val('General','LongName');
   $smtp->datasend(" Long Name:        $temp\n");
   $temp = $ini[$game]->val('General','Status');
   $smtp->datasend(" Status:           $temp\n");
   $temp = $ini[$game]->val('Running','Frequency');
   $smtp->datasend(" Turns run every:  $temp");
   $smtp->datasend(", or as soon as everybody's turn is in.") if (uc($ini[$game]->val('Running','RunEarly')) eq 'YES');
   $smtp->datasend("\n\nDescription:\n");
   $smtp->datasend("-----------\n");
   $smtp->datasend(shift(@description)."\n") while (@description);

   my @races = RaceNames($game);
   if (uc($ini[$game]->val('General','Status')) eq 'OPEN' ||
       uc($ini[$game]->val('General','Status')) eq 'INVITATION ONLY') {
      $smtp->datasend("\nRaces still available:\n---------------------");
      foreach my $player (1..11) {
         $smtp->datasend("\n $player - $races[$player-1]") if ( uc($ini[$game]->val("Player$player",'Active')) eq 'NO');
      }
      $smtp->datasend("\n\nRaces already taken:\n-------------------");
   } else {
      $smtp->datasend("\nRaces in play:\n-------------");
   }
   my $showPlayers = 0;
   $showPlayers = 1 if (uc($ini[$game]->val('General','HiddenPlayers')) eq 'NO');
   $showPlayers = 1 if (defined($auth) && $auth == -1);   # SysAdmin
   $showPlayers = 1 if (defined($auth) && $auth == 0 && $authgame == $game); # Admin of this game

   foreach my $player (1..11) {
      if (uc($ini[$game]->val("Player$player",'Active')) ne 'NO') {
         if ($showPlayers) {
            my $playername = $ini[$game]->val("Player$player",'Email');
            $playername =~ s/@.*$//;
            $smtp->datasend("\n $player - $races[$player-1] <$playername>");
         } else {
            $smtp->datasend("\n $player - $races[$player-1]");
         }
      }
   }

   $smtp->datasend("\n");
   $smtp->dataend();
   $smtp->quit;
}

sub TurnNumber {
   my $game = shift;
   my $none = $ini[$game]->val('General','Homedir') . "nextturn.hst";

   open(FILE,$none) or return 0;
   binmode FILE;
   sysread FILE,$none,20,0;
   close FILE;
   return unpack('x18S',$none);
}

sub checkbody {
   my $smtp = shift;
   my $game = shift;
   my $status = $ini[$game]->val('General','Status');

   if (uc($status) eq 'OPEN' || uc($status) eq 'INVITATION ONLY') {
      $smtp->datasend("\n Sorry, this game has not started yet.\n");
   } else {
      my @races;
      my $none;
      my $turnnumber = TurnNumber($game);
   
      @races = RaceNames($game);
      $smtp->datasend("\nWe are currently on turn $turnnumber.\n");
      $smtp->datasend("Races that have kindly submitted a .TRN file:\n");
      $none=1;
      foreach my $player (1..11) {
         my $fn;

         if (uc($ini[$game]->val("Player$player",'Active')) eq 'YES') {
            $fn = $ini[$game]->val('General','Homedir');
            $fn .= "player$player.trn";
            $smtp->datasend("    $races[$player-1]\n"), $none=0 if (-e $fn);
         }
      }
      $none and $smtp->datasend("    None.\n");
      $smtp->datasend("\nRaces that have not yet submitted a .TRN file:\n");
      $none=1;
      foreach my $player (1..11) {
         my $fn;

         if (uc($ini[$game]->val("Player$player",'Active')) eq 'YES') {
            $fn = $ini[$game]->val('General','Homedir');
            $fn .= "player$player.trn";
            $smtp->datasend("    $races[$player-1]\n"), $none=0 if (!(-e $fn));
         }
      }
      $none and $smtp->datasend("    None.\n");
      $none=1;
      foreach my $player (1..11) {
         my $fn;

         if (uc($ini[$game]->val("Player$player",'Active')) eq 'COMP') {
            $fn = $ini[$game]->val('General','Homedir');
            $fn .= "player$player.trn";
            if (!(-e $fn)) {
               $none and $smtp->datasend("\nComputer played races:\n");
               $none=0;
               $smtp->datasend("    $races[$player-1]\n");
            }
         }
      }

      $smtp->datasend("\n  The host will");
      $smtp->datasend(" not") if ((uc($status) eq 'PAUSED') or ( uc($ini[$game]->val('Running','RunEarly')) ne 'YES') );
      $smtp->datasend(" run immediately upon reciept of\nany remaining turns.\n");
      if (uc($status) eq 'RUNNING') {
         my $date = UnixDate(ParseDate($ini[$game]->val('NextRun','NextRun')),"on %A, %B the %E at %i:%M%p");
         $smtp->datasend("\n  The host is scheduled to run in any case\n$date\n");
      } elsif (uc($status) eq 'PAUSED') {
         $smtp->datasend("\n  This game is currently Paused\n");
      }
   }
}

sub checkto {
   my $to = shift;
   my $gamename = shift;
   my $game = $UnShortName{lc($gamename)};
   my $smtp;
   my $date;

   my $shortname =$ini[$game]->val('General','ShortName');
   $smtp = EnvelopeTo($to, "Check on Game $shortname");

   logit(4,"$to is checking $shortname");

   checkbody($smtp, $game);

   $smtp->dataend();
   $smtp->quit;
}

sub authinvalidto {
   my $to = shift;
   my $ia = shift;
   my $smtp = EnvelopeTo($to, 'Authorization invalid');

   chomp($ia);
   logit(4,"$to failed authorization with command $ia.");
   $smtp->datasend("Authorization invalid.\nInvalid authorization attempt:\n\n$ia");

   $smtp->dataend();
   $smtp->quit;
}

sub invalidto {
   my $to = shift;
   my $ia = shift;
   my $smtp = EnvelopeTo($to, 'Invalid Command');

   chomp($ia);
   logit(4,"$to tried to $ia; invalid command.");
   $smtp->datasend("Invalid Command.\nThe following command is invalid, and has been ignored:\n\n$ia");

   $smtp->dataend();
   $smtp->quit;
}

sub invalidgame {
   my $to = shift;
   my $ia = shift;
   my $smtp = EnvelopeTo($to, 'Invalid Game Name Used');

   chomp($ia);
   logit(4,"$to tried to $ia; invalid game name.");
   $smtp->datasend("Invalid Game Name in command:\n\n$ia");

   $smtp->dataend();
   $smtp->quit;
}

sub UpdateConfig {
   my $game = shift;
   my $gameini = $mainini->val('GameINI',"Game$game");

   logit(6,"Updating config file $gameini.");
   $ini[$game]->WriteConfig($gameini.'.new.ini') or die("Cannot write new $gameini because:\n$!\n");
   unlink($gameini);
   rename($gameini.'.new.ini',$gameini);
   $initime[$game] = (stat($gameini))[9];
}

sub joingame {
   my $to = shift;
   my $jc = shift;
   my $smtp;
   my (@tojoin,$gamename,$pw);
   my ($c,$i);
   local $,;

   if ($jc !~ /^\s*join\W+(\w+):(\w+):(.+)$/i) {
      logit(4,"$to sent an invalid join command.");
      $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
      $smtp->datasend("\n  Invalid join command. Invalid command follows:\n\n $jc \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }

   $gamename=$1;
   $pw=$2;
   @tojoin = split (/:/,$3);

   my $game=$UnShortName{lc($gamename)};
   if (!defined($game)) {
      logit(4,"$to sent a join command with an invalid game name.");
      $smtp = EnvelopeTo($to, 'Invalid "join" command');

      $smtp->datasend("\n  Invalid join command - Game $gamename not found.\n  Invalid command follows:\n\n $jc \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }

   if (uc($ini[$game]->val('General','Status')) eq 'INVITATION ONLY') {
      if (!defined($main::cookie) || ($main::cookie ne GenerateCookie($game, 'invite', $to)) ) {
         logit(4,"$to tried to join $gamename with an invalid cookie.");
         $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
         $smtp->datasend("\n  Invalid join command - Game $gamename is an invitation\nonly game, and you did not provide a valid cookie.\n\n  Invalid command follows:\n\n $jc \n");
   
         $smtp->dataend();
         $smtp->quit;
         return;
      }
   } elsif (uc($ini[$game]->val('General','Status')) ne 'OPEN') {
      logit(4,"$to tried to join $gamename, but it has already begun.");
      $smtp = EnvelopeTo($to, 'Invalid "join" command');

      $smtp->datasend("\n  Invalid join command - Game $gamename is already running.\n  Invalid command follows:\n\n $jc \n");

      $smtp->dataend();
      $smtp->quit;
      return;
   }
   for (@tojoin) {
      $_=10 if /^a$/i;
      $_=11 if /^b$/i;
      $_ = int;
      if ($_ <1 || $_ > 11) {
         logit(4,"$to tried to join with an invalid player number.");
         $smtp = EnvelopeTo($to, 'Invalid "join" command');
   
         $smtp->datasend("\n  Invalid join command - $_ is an invalid player number.\n  Invalid command follows:\n\n $jc \n");
   
         $smtp->dataend();
         $smtp->quit;
         return;
      }
      
      #check to see if slot is empty.
      if (uc($ini[$game]->val("Player$_",'Active')) eq 'NO') {

         $ini[$game]->setval("Player$_",'Active','YES');
         $ini[$game]->setval("Player$_",'Password',$pw);
         $ini[$game]->setval("Player$_",'Email',$to);

         logit(3,"$to successfully joined $gamename as player $_");

         $smtp = EnvelopeTo($to, '"join" command successful');
   
         $smtp->datasend("\n  Join command successful - You have been added to \ngame $gamename as player $_");
         my @races = RaceNames($game);
         $smtp->datasend(", $races[$_-1]") if (@races);
         $smtp->datasend(".\n  Join command follows:\n\n $jc \n");
   
         $smtp->dataend();
         $smtp->quit;

         UpdateConfig($game);
         return;
      }
   }

   logit(3,"$to tried to join $gamename, but all their choices are already taken.");
   $smtp = EnvelopeTo($to, '"join" command unsuccessful');

   $smtp->datasend("\n  Join command unsuccessful - You have not been added to \ngame $gamename because all of your chosen races are already taken.\n  Join command follows:\n\n $jc \n");

   $smtp->dataend();
   $smtp->quit;
}

sub toadmin {

# Doesn't use EnvelopeTo because it needs a different 'From'.

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   my $to;
   my $from = shift;
   my $auth = shift;
   my $authgame = shift;

   logit(3,"$from is contacting an admin for $authgame.");
   if (defined $auth && defined $authgame) {
      $to = $ini[$authgame]->val('Admin','Email');
      $to = $mainini->val('General','SysadminEmail') if (uc($to) eq 'NONE');
   }

   $to ||= $mainini->val('General','SysadminEmail');

   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   $smtp->datasend("From: $from\n");                          # fix?
   $smtp->datasend("To: VGA Plantes Administrator <$to>\n");
   $smtp->datasend('Subject: [VGA Planets] User Request'."\n");
   $smtp->datasend("\n");

   $smtp->datasend(shift) while (@_);

   $smtp->dataend();
   $smtp->quit;
}

sub broadcast {

# Doesn't use EnvelopeTo because it needs a different 'From'.
# Also sends multiple emails on one connection

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));
   my $to;
   my $from;
   my ($temp);
   my $glob;
   my $auth = shift;
   my $authgame = shift;

   $temp = shift while ($temp !~ /^\s*broadcast\b/i);
   $glob .= ((shift)."\n") while (@_);   #Ha.

   if ($auth == -1) {     #sysadmin
      logit(4,"Sysadmin Broadcast");
      $from = "The VGAPd System Administrator";
      for (my $game=1; $game <= $NumberOfGames; $game++) {
         foreach my $player (1..11) {
            if (uc($ini[$game]->val("Player$player",'Active')) ne 'NO') {
               $to = $ini[$game]->val("Player$player","Email");
      
               $smtp->mail($mainini->val('EMail','SMTPUser'));
               $smtp->to($to);
               $smtp->data();
            
               $smtp->datasend("From: $from\n");
               $smtp->datasend("To: $to\n");
               $smtp->datasend('Subject: [VGA Planets] Broadcast Message'."\n");
               $smtp->datasend("\n");
            
               $smtp->datasend($glob);
            
               $smtp->dataend();
            }
         }
      }
   } elsif (defined $authgame) {               #regular admin
      logit(4,"Admin of ".$ini[$authgame]->val("General","ShortName")." Broadcast");
      $from = "The Administrator of game ".$ini[$authgame]->val("General","ShortName")."<".$ini[$authgame]->val("Admin","Email").">";
      foreach my $player (1..11) {
         if (uc($ini[$authgame]->val("Player$player",'Active')) ne 'NO') {
            $to = $ini[$authgame]->val("Player$player",'Email');
   
            $smtp->mail($mainini->val('EMail','SMTPUser'));
            $smtp->to($to);
            $smtp->data();
         
            $smtp->datasend("From: $from\n");
            $smtp->datasend("To: $to\n");
            $smtp->datasend('Subject: [VGA Planets] Broadcast Message'."\n");
            $smtp->datasend("\n");
         
            $smtp->datasend($glob);
         
            $smtp->dataend();
         }
      }
   }

   $smtp->quit;
}

sub unsubmit {
   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $turnnumber = TurnNumber($game);

   my $fndst;

   $fndst = "player$player.trn";
   logit(4,"$to is unsubmitting $fndst of game $game.");
   unlink($ini[$game]->val('General','Homedir').$fndst) if (-e $ini[$game]->val('General','Homedir').$fndst);

   my $shortname = $ini[$game]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Unsubmit command success; turn $turnnumber of $shortname removed.\n");
      
   $smtp->datasend("  Your .TRN file has been deleted.\n");
   $smtp->datasend("  Everybody else is still waiting for your turn. Plese submit soon.\n");
   $smtp->datasend("\n  For your convenience, here is a check on game $shortname:\n\n");
   checkbody($smtp, $game);
   $smtp->dataend;
   $smtp->quit;
}

sub getTRN {
   my $game = shift;
   my $player = shift;
   my $nump = shift;
   my $entity = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my ($ent,$io,$line);
   my $cwd;
   my $turnnumber = TurnNumber($game);
   my $phost = 0;

   my $missing = 128;
   my $stale = 1;
   my $truncated = 128;
   my $damaged = 2;
   my $wrongplayer = 128;
   my $badchecksum = 2;
   my $yellow = 128;
   my $red = 3;
   my $lastknown = 3;

   if ($ini[$game]->val('General','HostExecutable') =~ /phost/i) {
      $phost = 1;
      $missing = 1;
      $stale = 2;
      $truncated = 4;
      $damaged = 8;
      $wrongplayer = 16;
      $badchecksum = 32;
      $yellow = 64;
      $red = 128;
      $lastknown = 128;
   }

   my ($fndst, $fnold);

   $fndst = "player$player.trn";
   $fnold = $fndst.'.OLD';

   rename($ini[$game]->val('General','Homedir').$fndst, $ini[$game]->val('General','Homedir').$fnold) if (-e $ini[$game]->val('General','Homedir').$fndst);

   foreach my $part (1..$nump) {
      $ent = $entity->parts($part);
      next if $ent->effective_type =~ /^text/;

      if ($io = $ent->open("r")) {
         open (FILE,'>'.$ini[$game]->val('General','Homedir').$fndst) or return;
         binmode FILE;
         while (defined ($line = $io->getline)) {
            print FILE $line;
         }
         $io->close;
         close FILE;
      }

      $cwd = cwd();
      
      chdir($ini[$game]->val('General','VGAPdir'));

      logit(4,"Checking $to turn file for $game.");
      if ($phost) {
         logit(5,'Running phost -Q -c '."$player ".
                  $ini[$game]->val('General','Homedir')
                  .' from '. cwd() );
   
         $io = system('phost -Q -c '."$player ".
                      $ini[$game]->val('General','Homedir'));
         $io /= 256; # Or should this be outside the if?
                     # I seem to recall the old windows perl not needing
                     # the divide.
                     # ...but this is my memory.
      } else {
         my $token=$player;
         $token='a' if ($player == 10);
         $token='b' if ($player == 11);
   
         logit(5,'Running chktrn '.
                  $ini[$game]->val('General','Homedir')." /$token"
                  .' from '. cwd() );
   
         $io = system('chktrn '.
                      $ini[$game]->val('General','Homedir')." /$token");
      }

      logit(6,"Changing to $cwd");
      chdir($cwd);

      logit(6,'Now back in '. cwd() );

      logit(6,"chktrn returned $io");

      if ($io == $missing || $io == $truncated || $io == $damaged
       || $io == $wrongplayer || $io == $badchecksum || $io > $lastknown) {

         logit(4,"$to turn is corrupt!");
         my $smtp = EnvelopeTo($to, 'Submit command error!');
      
         my $shortname = $ini[$game]->val('General','ShortName');
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $shortname\" <$to>\n");

         $smtp->datasend("  Error: CHKTRN reports that your .TRN file was corrupted.\n");
         $smtp->datasend("         Please try re-submitting turn $turnnumber of game $shortname.\n");
         $smtp->dataend;
         $smtp->quit;

         unlink($ini[$game]->val('General','Homedir').$fndst);
         if (-e $ini[$game]->val('General','Homedir').$fnold) {
            rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst);
         }
      } elsif ($io == $stale) {
         logit(4,"$to turn is stale!");
         my $smtp = EnvelopeTo($to, 'Submit command error!');
      
         my $shortname = $ini[$game]->val('General','ShortName');
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $shortname\" <$to>\n");
      
         $smtp->datasend("  Error: CHKTRN reports that your .TRN file was stale.\n");
         $smtp->datasend("         Please try submitting the correct, most recent, turn.\n");
         $smtp->datasend("         This game is $shortname; The current turn number is $turnnumber.\n");

         $smtp->dataend;
         $smtp->quit;

         unlink($ini[$game]->val('General','Homedir').$fndst);
         if (-e $ini[$game]->val('General','Homedir').$fnold) {
            rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst);
         }
      } else {
         logit(4,"$to turn has been accepted.");
         unlink ($fnold) if (-e $fnold);
   
         my $shortname = $ini[$game]->val('General','ShortName');
         my $smtp = EnvelopeTo($to, "Submit command success; turn $turnnumber of $shortname approved.\n");
      
#         $smtp->datasend("To: \"Player $player");
#         @races = RaceNames($game);
#         $smtp->datasend(", $races[$player-1],") if (defined @races);
#         $smtp->datasend(" of game $shortname\" <$to>\n");
      
         $smtp->datasend("  Your .TRN file has been accepted for processing.\n");
         $smtp->datasend("  You will be notified when the turn is run. Thank You.\n");
         $smtp->datasend("\n  For your convenience, here is a check on game $shortname:\n\n");
         checkbody($smtp, $game);
         $smtp->dataend;
         $smtp->quit;

         if ($io == $red || $io == $yellow) {
            $to = $ini[$game]->val('Admin','Email');
            my $subject;
            if ($io == $yellow) {
               $subject="Player $player is acting weird.\n";
            } else {
               $subject="Player $player is attempting to cheat.\n";
            }
            my $smtp = EnvelopeTo($to, $subject);
         
            my $shortname = $ini[$game]->val('General','ShortName');
#            $smtp->datasend("To: Administrator of game $shortname <$to>\n");
         
            $smtp->datasend("  According to CHKTRN, player $player");
            my @races = RaceNames($game);
            $smtp->datasend(", $races[$player-1],") if (@races);
            if ($io == $yellow) {
               $smtp->datasend(" may\n be attempting to cheat, but probably not.\n This is in");
            } else {
               $smtp->datasend(" is\nattempting to cheat in");
            }
            $smtp->datasend(" game $shortname, on turn $turnnumber.\n");
            $smtp->datasend("Feel free to react as you see fit.\n");
            $smtp->dataend;
            $smtp->quit;
         }
         unlink ($ini[$game]->val('General','Homedir').$fnold) if (-e $ini[$game]->val('General','Homedir').$fnold);
      }

      return;
   }

   logit(4,"$to did a sumbit, but there was no attachment.");
   my $smtp = EnvelopeTo($to, 'Submit command error!');

   my $shortname = $ini[$game]->val('General','ShortName');
#   $smtp->datasend("To: \"Player $player of game $shortname\" <$to>\n");

   $smtp->datasend("  Error: No binary attachment found in email.\n");
   $smtp->datasend("         Please try attaching a turn file next time.\n");
   $smtp->datasend("\n         (in case you were wondering, I was expecting\n          turn $turnnumber of $shortname)\n");
   $smtp->dataend;
   $smtp->quit;

   rename($ini[$game]->val('General','Homedir').$fnold,$ini[$game]->val('General','Homedir').$fndst) if (-e $ini[$game]->val('General','Homedir').$fnold);
}

sub sendRST {

# Doesn't use EnvelopeTo because we need MIME

   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $filename;
   my $gamename;
   my $turnnumber = TurnNumber($game);

   my $glob;
   local $/;

   $filename = $ini[$game]->val('General','Homedir') . "player$player.rst";

   if (!open(FILE,$filename)) {
      # Presume sysadmin ran killrace, or PAAGhoul got 'em or something.
      logit(1, "The RST of Player $player won't open. Presuming dead.");
      $ini[$game]->setval("Player$player",'Active','NO');
      UpdateConfig($game);
      return;
   }
   binmode FILE;
   $glob = <FILE>;
   close FILE;

   my $smtp = Net::SMTP->new($mainini->val('EMail','SMTP'));

   $smtp->mail($mainini->val('EMail','SMTPUser'));
   $smtp->to($to);
   $smtp->data();

   $gamename = $ini[$game]->val('General','ShortName');

   my $from = $mainini->val('General','VGAPEmail');
   $smtp->datasend("From: \"Pete\'s Automated VGAP Thingy\" <$from>\n");
   $smtp->datasend("To: \"Player $player of game $gamename\" <$to>\n");
   $smtp->datasend('Subject: [VGA Planets] RST file for '."turn $turnnumber of $gamename.\n");
   $smtp->datasend('MIME-Version: 1.0'."\n");
   $smtp->datasend('Content-Type: multipart/mixed; boundary="troZ"'."\n");
   $smtp->datasend("\n");
   $smtp->datasend("This is a multi-part message in MIME format.\n");

   $filename = $ini[$game]->val('General','Homedir') . "util$player.dat";

   $smtp->datasend("\n--troZ\n");
   $smtp->datasend("Content-Type: text/plain\n");
#   $smtp->datasend("Content-Disposition: inline\n");
   $smtp->datasend("Content-Transfer-Encoding: 7bit\n");
   $smtp->datasend("\n");
   if (-e $filename) {
      $smtp->datasend("Here are your files for turn $turnnumber of $gamename\n");
   } else {
      $smtp->datasend("Here is your file for turn $turnnumber of $gamename\n");
   }

   $smtp->datasend("\n--troZ\n");
   $smtp->datasend('Content-Type: application/octet-stream; name="'."PLAYER$player.RST"."\"\n");
   $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
   $smtp->datasend('Content-Disposition: attachment; filename="'."PLAYER$player.RST"."\"\n");
   $smtp->datasend("\n");

   $smtp->datasend(encode_base64($glob));

   if (open(FILE,$filename) ) {
     binmode FILE;
     $glob = <FILE>;
     close FILE;

     $smtp->datasend("\n--troZ\n");
     $smtp->datasend('Content-Type: application/octet-stream; name="'."UTIL$player.DAT\"\n");
     $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
     $smtp->datasend('Content-Disposition: attachment; filename="'."UTIL$player.DAT\"\n");
     $smtp->datasend("\n");

     $smtp->datasend(encode_base64($glob));
   }

   $filename = $ini[$game]->val('General','Homedir') . "xyplan$player.dat";
   if (open(FILE,$filename) ) {
     binmode FILE;
     $glob = <FILE>;
     close FILE;

     $smtp->datasend("\n--troZ\n");
     $smtp->datasend('Content-Type: application/octet-stream; name="'."XYPLAN.DAT\"\n");
     $smtp->datasend('Content-Transfer-Encoding: base64'."\n");
     $smtp->datasend('Content-Disposition: attachment; filename="'."XYPLAN.DAT\"\n");
     $smtp->datasend("\n");

     $smtp->datasend(encode_base64($glob));
   }

   $smtp->datasend("\n--troZ--\n");
   $smtp->dataend;
   $smtp->quit;

   logit(4,"player$player.rst sent!");
}

sub warnplayer {
   my $game = shift;
   my $player = shift;
   my $to = $ini[$game]->val("Player$player",'Email');
   my $time;

   my $gamename = $ini[$game]->val('General','ShortName');
   my $smtp = EnvelopeTo($to, "Warning about $gamename!");

   logit(4,"$to is being warned about $gamename");

#   $smtp->datasend("To: \"Player $player of game $a\" <$to>\n");

   $time = $ini[$game]->val('Running','Warning');
   $smtp->datasend("  Warning! The host will be running game $gamename in $time, and\nwe don't have your .TRN file yet.\n");

   $smtp->dataend();
   $smtp->quit;
}

sub warngame {
   my $game = shift;

   $ini[$game]->setval('NextRun','NextWarn','Never');
   UpdateConfig($game);

   foreach my $player (1..11) {
      my $fn;

      if (uc($ini[$game]->val("Player$player",'Active')) eq 'YES') {
         $fn = $ini[$game]->val('General','Homedir');
         $fn .= "player$player.trn";
         if (! (-e $fn)) {
            warnplayer($game,$player);
         }
      }
   }
}

sub NextRunTime {
   my $next = shift;
   my $game = shift;
   my $now = ParseDate('today');

   $next = DateCalc($next,'+'.$ini[$game]->val('Running','Frequency'));
   my @skip = split(/:/,$ini[$game]->val('Running','Exempt'));
   my $flag = 1;
   while ($flag) {
      $flag = 0;
      foreach my $skip (@skip) {
         if ($skip) {
            if (Date_GetNext($now,$skip) le $next) {
               $next = DateCalc($next,'+ 24 hours');
               $flag = 1;
               $skip = 0;
            }
         }
      }
   }

   return $next;
}

sub rungame {
   my $game = shift;
   my $gamename = $ini[$game]->val('General','ShortName');
   my $now = ParseDate('today');
   my $cmdline;
   my $compline;
   my $compon = 1;
   my ($next,$skip);
   my @skip;
   my $cwd;

   logit(4,"Running Host for game $gamename.");

   $cmdline = $ini[$game]->val('General','HostExecutable');
   $cmdline .= ' '.$ini[$game]->val('General','Homedir');

   $compline = $ini[$game]->val('General','ComputerPlayer');
   $compon = 0 if (length($compline) < 1);
   $compline .= ' '.$ini[$game]->val('General','Homedir');
   $compline .= ' /';

   my $runcomputer = 0;
   foreach my $player (1..11) {
      if (uc($ini[$game]->val("Player$player",'Active')) eq 'COMP' and !(-e $ini[$game]->val('General','Homedir')."player$player.trn")) {
         my $token = $player;
         $token = 'a' if ($player == 10);
         $token = 'b' if ($player == 11);
         $compline .= "$token";
         $runcomputer = 1;
      }
   }
# Uncomment the next line for unregistered computer players
#   $compline .= '+';

   $cwd = cwd();
   chdir($ini[$game]->val('General','VGAPdir'));   #sigh.

   `$compline` if ($runcomputer && $compon);
   logit(2,"Host running for $gamename.");
   `$cmdline`;

   chdir($cwd);

   $next = ( (shift)?$now:$ini[$game]->val('NextRun','NextRun') );
   $ini[$game]->setval('NextRun','LastRun',$next);

   $next = NextRunTime($next, $game);

   $ini[$game]->setval('NextRun','NextRun',$next);
   $ini[$game]->setval('NextRun','NextWarn',DateCalc($next,'-'.$ini[$game]->val('Running','Warning') ));

   UpdateConfig($game);

   my $sn = $ini[$game]->val('General','ShortName');
   chdir($mainini->val('Backup','Directory'));

   $cmdline = 'zip -9 -X -j ';
   if (uc($mainini->val('Backup','Style')) eq 'ROTATE') {
      unlink($sn.'5.zip');
      my $rotate;
      for ($rotate=4;$rotate>=1;$rotate--) {
         rename $sn.$rotate.'.zip',
                $sn.($rotate+1).'.zip';
      }
      rename $sn.'.zip',
             $sn.'1.zip';
      $cmdline .= $sn;
   } else {
      mkdir $sn unless (-d $sn);
      chdir $sn;
      $cmdline .= 'turn'.TurnNumber($game);
   }
   $cmdline .= ' '.$ini[$game]->val('General','Homedir');
   $cmdline .= '*';
   `$cmdline`;

   chdir($cwd);

   foreach my $player (1..11) {
      sendRST($game,$player) if (uc($ini[$game]->val("Player$player",'Active')) ne 'NO')
   }
}

sub pausegame {
   my $game = shift;
   my $from = shift;
   my $gamename = $ini[$game]->val('General','ShortName');
   my @email;
   my $admin = $ini[$game]->val('Admin','Email');

   push @email, $admin;
   push @email, $from if ($from ne $admin);

   $ini[$game]->setval('General','Status','Paused');
   $ini[$game]->setval('NextRun','NextRun','Never');
   $ini[$game]->setval('NextRun','NextWarn','Never');
   UpdateConfig($game);

   logit(2,"$gamename has been paused.");
   foreach my $mail ( @email ) {
      my $smtp = EnvelopeTo($mail, "Pausing $gamename");
      $smtp->datasend("$gamename is now paused.\n");

      $smtp->dataend();
      $smtp->quit;
   }
}

sub resumegame {
   my $game = shift;
   my $from = shift;
   my $next = ParseDate('today');
   my $gamename = $ini[$game]->val('General','ShortName');
   my @email;
   my $admin = $ini[$game]->val('Admin','Email');

   $next = NextRunTime($next, $game);

   push @email, $admin;
   push @email, $from if ($from ne $admin);

   $ini[$game]->setval('General','Status','Running');
   $ini[$game]->setval('NextRun','NextRun',$next);
   $ini[$game]->setval('NextRun','NextWarn',DateCalc($next,'-'.$ini[$game]->val('Running','Warning') ));
   UpdateConfig($game);

   logit(2,"$gamename has been resumed.");

   foreach my $mail ( @email ) {
      my $smtp = EnvelopeTo($mail, "Resuming $gamename");
      $smtp->datasend("$gamename is now resumed. Details:\n");

      checkbody($smtp, $game);

      $smtp->dataend();
      $smtp->quit;
   }
}

sub IniLoad {

   -e $ininame or die ("Can't open $ininame.\n");

   $mainini = new IniConf( -file => $ininame ) or die("Cannot open $ininame because:\n$!\n");

   die ("Can't open $ininame.\n") if (!defined($mainini)); # Stupidity check. Enjoy it. There aren't too many of these.

# Keep or remove? Hmmmmm...
   $LogFileName= $mainini->val('General','LogFileName');
   $DebugLog= $mainini->val('General','LogFileDebugLevel');
   $DebugCon= $mainini->val('General','ConsoleDebugLevel');

   if ($maininitime == -1) {
      logit(0,"VGAPd starting up.");
   } else {
      logit(1,"INI File manually modified while host running!");
   }

   $maininitime=(stat($ininame))[9];

   $POPhost= $mainini->val('EMail','POP');
   $POPuser= $mainini->val('EMail','POPUser');
   $POPpw= $mainini->val('EMail','POPpw');

   $MainDelay= $mainini->val('General','CheckEvery');

   die("Error: Must supply delay value!\n") if (!defined $MainDelay);

   $NumberOfGames= $mainini->val('General','NumberOfGames');
   return unless ($NumberOfGames >= 1);

   foreach my $game (1..$NumberOfGames) {

      my $gameini= $mainini->val('GameINI',"Game$game");
      $initime[$game]=(stat($gameini))[9];
      $ini[$game]=new IniConf( -file => $gameini ) or die("Cannot open $gameini because:\n$!\n");
      $ini[$game]->SetWriteMode($mainini->val('General','IniWriteMode'));

      $UnShortName{lc($ini[$game]->val('General','ShortName'))}=$game;
   }
}

$ininame = 'vgapd.ini' if (!defined($ininame=shift));

my $parser = new MIME::Parser;
$parser->output_to_core('ALL'); #For now.

$running = 1;
$maininitime = -1; #supress warning about " != on undefined" below

$SIG{'INT'} = sub { $running = 0 };
$SIG{'TERM'} = sub { $running = 0 };
$SIG{'QUIT'} = sub { $running = 0 };
$SIG{'HUP'} = 'IGNORE';

VGAPd: while (1) {
   my $i;
   my $nonVGAP;
   my $pop;

   $nonVGAP = 0;

   IniLoad if ($maininitime != (stat($ininame))[9]);
   for (my $game=1; $game <= $NumberOfGames; $game++) {
      IniLoad if ($initime[$game] != (stat( $mainini->val('GameINI',"Game$game") ))[9]);
   }

   undef $pop;

   logit(6," - Mail hammering loop");
   while (!defined($pop))  { $pop = Net::IMAP::Simple->new($POPhost) or sleep(5); }
   logit(6," - Mail login");
   $pop->login($POPuser,$POPpw);
#   @pstat=$pop->popstat();
#   $messageCount = $pstat[0];
   my $messageCount = $pop->select('Inbox');
   logit(5,"Checking Mail server. Number of messages: $messageCount");
   for ($i=1;$i<=$messageCount;$i++) {
      my $topr=$pop->top($i,1);
      for (@$topr) {
         if (/^Subject:/i) {
            logit(6,$_);
            if(/VGA ?P/i) {  #must be case insensitive, because Outlook Express is.

               # I'd love to do a fork here, but Perl doesn't support fork
               # under windoze (yet).
               # 5.005 is rumoured to support threads [ie. async { } ]
               # but it's not out yet (and I don't know if they will work
               # on 95-NT).

               # Revision: 5.005 is out now. But I'm too lazy to install it.

               my $commandsfound=0;
               local $main::cookie=undef;
               my $auth = undef;
               my $authgame = undef;

               if ($pop->list($i) > 256000) { #ignore messages over approx 256K
                  $pop->delete($i);
                  next;
               }

               my $msgr=$pop->get($i);

               logit(6,@$msgr);
   
               my $entity = $parser->parse_data(\@$msgr);
               my $ent;
               my $type = $entity->effective_type;
               my $nump = $entity->parts;
               my $head=$entity->head;
               logit(8,$head->as_string);
               logit(8,$entity->as_string);
               my $from=$head->get('from');
               chomp($from);
               logit(7,$from);
               $from = $1 if ($from =~ /<(.+)>/);
               $from =~ s/\(.*?\)//g;
               logit (6,"Number of parts: $nump   Type: $type");
               if ($type =~ /^multipart/) {
                  $ent = $entity->parts(0);
               } else {
                  $ent=$entity;
               }
               $ent = $ent->parts(0) while $ent->effective_type =~ /^multipart/;
               $type = $ent->effective_type;

               $pop->delete($i);  #move this up here... delete even if we can't find a 'text/*' section.

               last if $type !~ /^text/;
   
               my $body = $ent->bodyhandle;
               my $text = $body->as_string;
               my @text = split(/\n/,$text);

               for (@text) {
                  chomp;
                  logit(6," - ",$_);
                  if (/^\s*help\b/i) {
                     $commandsfound++;
                     helpto($from) if ($from);
                  }
                  if (/^\s*list\b/i) {
                     $commandsfound++;
                     listto($from) if ($from);
                  }
                  if (/^\s*desc\s+(\w+)\s*$/i) {
                     $commandsfound++;
                     if ($from && defined $UnShortName{lc($1)}) {
                        descto($from, $UnShortName{lc($1)}, $auth, $authgame);
                     } else {
                        invalidgame($from,$_) if ($from);
                     }
                  }
                  if (/^\s*check\s+(\w+)\s*$/i) {
                     $commandsfound++;
                     if ($from && defined $UnShortName{lc($1)}) {
                        checkto($from,$1);
                     } else {
                        invalidgame($from,$_) if ($from);
                     }
                  }
                  if (/^\s*shutdown\b/i) {
                     if ($auth == -1) {
                        $commandsfound++;
                        $pop->quit();
                        last VGAPd;
                     }
                  }

                  if (/^\s*resend\b/i) {
                     $commandsfound++;
                     sendRST($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*email (.*\@.*)$/i) {
                     $commandsfound++;
                     logit (6,"Email command found ($_).");
                     if (defined ($auth) and $auth > 0) {
                        logit (3,"$from is changing e-mail of player $auth of game $authgame to $1.");
                        $ini[$authgame]->setval("Player$auth",'Email',$1);
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*pass\s+(.+)\s*$/i) {
                     $commandsfound++;
                     logit (6,"Pass command found ($_).");
                     if (defined ($auth) and $auth > 0) {
                        logit (3,"$from is changing password of player $auth of game $authgame.");
                        $ini[$authgame]->setval("Player$auth",'Password',$1);
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*resign\b/i) {
                     $commandsfound++;
                     logit (4,"Resign command found!");
                     if (defined ($auth) and $auth > 0) {
                        logit (3,"$from is trying to resign from player $auth of game $authgame.");
                        resign($auth, $authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*kill\s+(\d+)\s*$/i) {
                     $commandsfound++;
                     my $kill = $1;
                     logit (4,"Kill command found!");
                     if (defined ($auth) and $auth == 0) {
                        logit (3,"Admin ($from) killing Player $kill of game $authgame.");
                        killrace($kill, $authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*forcerun\s*$/i) {
                     $commandsfound++;
                     logit (4,"Forcerun command found!");
                     if (defined ($auth) and $auth == 0) {
                        logit (3,"Admin ($from) force running game $authgame.");
                        forcerun($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*submit\b/i) {
                     $commandsfound++;
# ignored now. Implied by attachment.                     getTRN($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*unsubmit\b/i) {
                     $commandsfound++;
                     unsubmit($authgame,$auth) if (defined ($auth) and $auth > 0);
                  }
                  if (/^\s*end\b/i) {
                     $commandsfound++;
                     last;
                  }
                  if (/^\s*comp\b/i) {
                     $commandsfound++;
                     if (defined $auth and $auth > 0) {
                        if (/on/i or /yes/i) {
                           logit(4,"$from is turning COMP on.");
                           $ini[$authgame]->setval("Player$auth",'Active','COMP');
                        } else {
                           logit(4,"$from is turning COMP off.");
                           $ini[$authgame]->setval("Player$auth",'Active','YES');
                        }
                        UpdateConfig($authgame);
                     }
                  }
                  if (/^\s*pause\b/i) {
                     $commandsfound++;
                     if (defined $auth && $auth == 0 && uc($ini[$authgame]->val('General','Status')) eq 'RUNNING') {
                        logit(5,"$from is pausing $authgame.");
                        pausegame($authgame, $from);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*resume\b/i) {
                     $commandsfound++;
                     if (defined $auth && $auth == 0 && uc($ini[$authgame]->val('General','Status')) eq 'PAUSED') {
                        logit(4,"$from is resuming $authgame.");
			resumegame($authgame, $from);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*start\b/i) {
                     my $status;
                     $commandsfound++;
                     defined $authgame and $status = $ini[$authgame]->val('General','Status');
                     if (defined $auth && $auth == 0 && (uc($status) eq 'OPEN' || uc($status) eq 'INVITATION ONLY')) {
                        logit(4,"$from is starting $authgame.");
                        $ini[$authgame]->setval('General','Status','Running');
                        $ini[$authgame]->setval('NextRun','NextRun',ParseDate('today'));
                        UpdateConfig($authgame);
                     } else {
                        invalidto($from,$_) if ($from);
                     }
                  }
                  if (/^\s*admin\b/i) {
                     $commandsfound++;

                     toadmin($from, $auth, $authgame, @$msgr);
                     last;
                  }

                  if (/^\s*cookie\s+(\S+)\s*$/i) {
                     $commandsfound++;
                     $main::cookie = $1;
                  }
                  if (/^\s*invite\s+(\S+)\s*$/i) {
                     my $to = $1;
                     $commandsfound++;
                     if (defined $auth && $auth == 0) {
                        invite($to,$authgame);
                     } else {
                        invalidto($from, $_) if ($from);
                     }
                  }

                  if (/^\s*auth\b/i) {
                     my ($game, $user, $pass);

                     /^\s*auth\s+([\w:]+)/i;
                     logit(5,"Auth command found, parsing.");
                     ($game,$user,$pass)=split /:/,$1;
                     logit(6,"Game: $game; User: $user; Pass: $pass");

                     $commandsfound++;

                     if ($user eq 'sysadmin') {
                        logit(2,"$from attempting sysadmin login");
                        if ($pass eq $mainini->val('General','SysadminPW')) {
                           $authgame=$UnShortName{lc($game)};
                           $auth = -1;                #Is this what I want?
                        } else {
                           logit(0,"$from: UNSUCCESSFUL SYSADMIN LOGIN ATTEMPT!");
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     } elsif ($user eq 'admin') {
                        $authgame=$UnShortName{lc($game)};
                        logit(3,"$from attempting admin login to game $game");
                        if (defined $authgame) {
                           if ($pass eq $ini[$authgame]->val('Admin','Password')) {
                              $auth=0;
                           } else {
                              logit(1,"$from: UNSUCCESSFUL ADMIN LOGIN ATTEMPT! :$game");
                              undef $auth;
                              authinvalidto($from,$_) if ($from);
                           }
                        } else {
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     } else {
                        $user =~ s/^a$/10/i;
                        $user =~ s/^b$/11/i;
                        $authgame=$UnShortName{lc($game)};
                        if (defined $authgame &&
                              $user>0 && $user<12 &&
                              uc($ini[$authgame]->val("Player$user",'Active')) ne 'NO') {
                           if ($pass eq $ini[$authgame]->val("Player$user",'Password')) {
                              $auth=$user;
                           } else {
                              undef $auth;
                              authinvalidto($from,$_) if ($from);
                              logit(3,"$from: Invalid Password. :$game:$user");
                           }
                        } else {
                           undef $auth;
                           authinvalidto($from,$_) if ($from);
                        }
                     }
                     logit(4,"$from authorized as $auth of $authgame.");
                  } #end Auth

                  if (/^\s*join\b/i) {
                     $commandsfound++;
                     joingame($from,$_) if ($from);
                  }

                  if (/^\s*broadcast\b/i) {
                     if (defined $auth) {
                        if ($auth < 1) {
                           $commandsfound++;

                           broadcast($auth, $authgame, @text);
                        }
                     }
                     last;
                  }

               }
   
               #while (shift @$msgr ne "\n") {};
               #print @$msgr;
               getTRN($authgame,$auth, $nump, $entity) if ( ($nump > 1) && defined ($auth) and $auth > 0);

               if (!$commandsfound) {
                  $head=$ent->head;
                  $from=$head->get('from');
                  chomp($from);
                  $from = $1 if ($from =~ /<(.*)>/);
                  $from =~ s/\(.*?\)//g;
                  helpto($from,1) if ($from);
               }

               last;
            }
            else { $nonVGAP++; }
         }
      }
   }
   $pop->quit();

# Check for active games that can be run here.

   for (my $game=1; $game <= $NumberOfGames; $game++) {
      if (uc($ini[$game]->val('NextRun','NextRun')) ne 'NEVER' && uc($ini[$game]->val('General','Status')) eq 'RUNNING') {
         if (ParseDate($ini[$game]->val('NextRun','NextRun')) le ParseDate('today')) {
            rungame($game,0);
         } elsif (uc($ini[$game]->val('Running','RunEarly')) eq 'YES') {
            my $run = 1;
            foreach my $player (1..11) {
               my $fn;

               if (uc($ini[$game]->val("Player$player",'Active')) eq 'YES') {
                  $fn = $ini[$game]->val('General','Homedir');
                  $fn .= "player$player.trn";
                  if (! (-e $fn)) {
                     $run = 0;
                     last;
                  }
               }
            }
            rungame($game,1) if ($run);
         }
      }
      if (uc($ini[$game]->val('NextRun','NextWarn')) ne 'NEVER') {
         if (ParseDate($ini[$game]->val('NextRun','NextWarn')) le ParseDate('today')) {
            warngame($game);
         }
      }
   }

   last VGAPd if (!$running);
   sleep($MainDelay);
   last VGAPd if (!$running);
}

logit(1,"Clean Shutdown");
